const toString = Object.prototype.toString

/**
 * 检查值的类型是否匹配
 * @param val
 * @param type
 * @returns {boolean} true 表示匹配成功，false 表示匹配失败
 */
export const is = (val: unknown, type: string) => {
  return toString.call(val) === `[object ${type}]`
}

/**
 * 检查值是否已定义
 * @param val
 * @returns {boolean} true 表示已定义，false 表示未定义
 */
export const isDef = <T = unknown>(val?: T): val is T => {
  return val !== undefined
}

/**
 * 检查值是否未定义
 * @param val
 * @returns {boolean} true 表示未定义，false 表示已定义
 */
export const isUnDef = <T = unknown>(val?: T): val is T => {
  return !isDef(val)
}

/**
 * 检查值是否是对象
 * @param val
 * @returns {boolean} true 表示是对象，false 表示不是对象
 */
export const isObject = (val: any): val is Record<any, any> => {
  return val !== null && is(val, 'Object')
}

/**
 * 检查值是否为空
 * @param val
 * @returns {boolean} true 表示为空，false 表示不为空
 */
export const isEmpty = <T = unknown>(val: T): val is T => {
  if (val === null) {
    return true
  }
  if (isArray(val) || isString(val)) {
    return val.length === 0
  }

  if (val instanceof Map || val instanceof Set) {
    return val.size === 0
  }

  if (isObject(val)) {
    return Object.keys(val).length === 0
  }

  return false
}

/**
 * 检查值是否是日期对象
 * @param val
 * @returns {boolean} true 表示是日期对象，false 表示不是日期对象
 */
export const isDate = (val: unknown): val is Date => {
  return is(val, 'Date')
}

/**
 * 检查值是否为null
 * @param val
 * @returns {boolean} true 表示为null，false 表示不为null
 */
export const isNull = (val: unknown): val is null => {
  return val === null
}

/**
 * 检查值是否同时为null和undefined
 * @param val
 * @returns {boolean} true 表示为null或undefined，false 表示不为null或undefined
 */
export const isNullAndUnDef = (val: unknown): val is null | undefined => {
  return isUnDef(val) && isNull(val)
}

/**
 * 检查值是否为null或undefined
 * @param val
 * @returns {boolean} true 表示为null或undefined，false 表示不为null或undefined
 */
export const isNullOrUnDef = (val: unknown): val is null | undefined => {
  return isUnDef(val) || isNull(val)
}

/**
 * 检查值是否为数字
 * @param val
 * @returns {boolean} true 表示为数字，false 表示不为数字
 */
export const isNumber = (val: unknown): val is number => {
  return is(val, 'Number')
}

/**
 * 检查值是否为Promise对象
 * @param val
 * @returns {boolean} true 表示为Promise对象，false 表示不为Promise对象
 */
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
  return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
}

/**
 * 检查值是否为字符串
 * @param val
 * @returns {boolean} true 表示为字符串，false 表示不为字符串
 */
export const isString = (val: unknown): val is string => {
  return is(val, 'String')
}

/**
 * 检查值是否为函数
 * @param val
 * @returns {boolean} true 表示为函数，false 表示不为函数
 */
export const isFunction = (val: unknown): val is (...args: any[]) => any => {
  return typeof val === 'function'
}

/**
 * 检查值是否为布尔值
 * @param val
 * @returns {boolean} true 表示为布尔值，false 表示不为布尔值
 */
export const isBoolean = (val: unknown): val is boolean => {
  return is(val, 'Boolean')
}

/**
 * 检查值是否为正则表达式
 * @param val
 * @returns {boolean} true 表示为正则表达式，false 表示不为正则表达式
 */
export const isRegExp = (val: unknown): val is RegExp => {
  return is(val, 'RegExp')
}

/**
 * 检查值是否为数组
 * @param val
 * @returns {boolean} true 表示为数组，false 表示不为数组
 */
export const isArray = (val: any): val is Array<any> => {
  return val && Array.isArray(val)
}

/**
 * 检查值是否为Window对象
 * @param val
 * @returns {boolean} true 表示为Window对象，false 表示不为Window对象
 */
export const isWindow = (val: any): val is Window => {
  return typeof window !== 'undefined' && is(val, 'Window')
}

/**
 * 检查值是否为DOM元素
 * @param val
 * @returns {boolean} true 表示为DOM元素，false 表示不为DOM元素
 */
export const isElement = (val: unknown): val is Element => {
  return isObject(val) && !!val.tagName
}

/**
 * 检查值是否为Map对象
 * @param val
 * @returns {boolean} true 表示为Map对象，false 表示不为Map对象
 */
export const isMap = (val: unknown): val is Map<any, any> => {
  return is(val, 'Map')
}

/**
 * 检查当前环境是否为服务器端
 */
export const isServer = typeof window === 'undefined'

/**
 * 检查当前环境是否为客户端
 */
export const isClient = !isServer

/**
 * 检查字符串是否为有效的URL
 * @param path
 * @returns {boolean} true 表示为有效的URL，false 表示为无效的URL
 */
export const isUrl = (path: string): boolean => {
  const reg = /^(https?:\/\/)?(?:[-:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www\.|[-:&=+$,\w]+@)[A-Za-z0-9.-]+(?:\/[+~%/.\w-]*)?(?:\?[-+=&%@.\w]*)?(?:#\w*)?$/
  return reg.test(path)
}

/**
 * 检查字符串是否为图片路径
 * @param path
 * @returns {boolean} true 表示为图片路径，false 表示为非图片路径
 */
export const isImgPath = (path: string): boolean => {
  return /(https?:\/\/|data:image\/).*?\.(png|jpg|jpeg|gif|svg|webp|ico)/i.test(path)
}

/**
 * 检查值是否为空（空字符串、null或undefined）
 * @param val
 * @returns {boolean} true 表示为空，false 表示不为空
 */
export const isEmptyVal = (val: any): boolean => {
  return val === '' || val === null || val === undefined
}

/**
 * 是否是外部链接
 * @param path 地址
 */
export const isExternal = (path: string) => /^(https?:|mailto:|tel:)/.test(path)

/**
 * 检查当前运行环境是否为Windows OS。
 *
 * 这个函数通过检查navigator.userAgent字符串来判断当前运行环境。
 * 如果userAgent字符串中包含"windows"或"win32"（不区分大小写），则认为当前环境是Windows OS。
 *
 * @returns {boolean} 如果当前环境是Windows OS，返回true，否则返回false。
 */
export const isWindowsOs = (): boolean => {
  const windowsRegex = /windows|win32/i
  return windowsRegex.test(navigator.userAgent)
}

/**
 * 检查当前运行环境是否为Mac OS。
 *
 * 这个函数通过检查navigator.userAgent字符串来判断当前运行环境。
 * 如果userAgent字符串中包含"macintosh"或"mac os x"（不区分大小写），则认为当前环境是Mac OS。
 *
 * @returns {boolean} 如果当前环境是Mac OS，返回true，否则返回false。
 */
export const isMacOs = (): boolean => {
  const macRegex = /macintosh|mac os x/i
  return macRegex.test(navigator.userAgent)
}
